from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from webdriver_manager.chrome import ChromeDriverManager

import lxml.html
from lxml import etree
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
from textblob import TextBlob

import time
import re
import csv
import random
from pathlib import Path

import config
from database import DataBase



class Browser:

    """ Класс представляющий браузер """

    def get_browser(self):

        """ Настроить и получить браузер """

        user_agent = UserAgent()
        user_agent = user_agent.opera
        driver = ChromeDriverManager().install()

        options = webdriver.ChromeOptions()
        options.add_argument(f'--user-agent={user_agent}')
        options.add_argument(f'--disable-blink-features=AutomationControlled')
        #options.add_argument('--headless')

        browser = webdriver.Chrome(
            driver,
            options=options
        )

        return browser


    def launch_browser(self, url, time=5):

        """ Запустить браузер """

        self.browser.get(url)
        self.browser.maximize_window()
        self.browser.implicitly_wait(time)


    def close_browser(self):

        """ Закрыть браузер """

        self.browser.close()



class Parser(Browser):

    """ Класс представляющий парсер """

    def __init__(self):

        self.home_page = 'https://stackoverflow.com/questions/'
        self.browser = self.get_browser()
        self.launch_browser(url=self.home_page)
        self.parsing_process()


    def convert_to_bs(self):

        """ Получить объект BeautifulsSoup из Selenium """

        html_source = self.browser.page_source
        page = BeautifulSoup(html_source, 'lxml')

        return page


    def get_bs_object(self, file_name):

        """ Открыть сохранненый файл страницы и вернуть его в виде объекта bs """

        page_file = Path('stackoverflow_html/')
        page_file = page_file/file_name

        with open(page_file, 'r', encoding='utf-8') as file:

            text = file.read()
            page = BeautifulSoup(text, 'lxml')

            return page


    def wait(self, t=2.6):

        """ Чуть-чуть подождать """

        t = random.uniform(1, t)

        print(f'Ждем {t}\n')
        time.sleep(t)


    def write_to_csv(self, data):

        """ Записать переданные данные в csv файл """

        with open('data.csv', 'a') as file:

            writer = csv.writer(file)

    def close_banners(self):

        """ Закрываем баннеры """

        self.browser.find_element(
            By.XPATH,
            '//div[@class="ff-sans ps-fixed z-nav-fixed ws4 sm:w-auto p32 sm:p16 bg-black-750 fc-white bar-lg b16 l16 r16 js-consent-banner"]//div[@class="grid gs8 ai-stretch fd-column sm:fd-row"]/button[1]'
        ).click()

        self.browser.implicitly_wait(5)


    def parsing_process(self):

        """ Весь процесс парсинга """

        def go_to_next_page(page_number):

            """ Перейти на следующую страницу вопросов """

            try:

                link = f'https://stackoverflow.com/questions?tab=newest&page={page_number}'
                self.launch_browser(url=link)

            except:

                return 0


        page_number = 1

        while True:

            links_all_questions = self.get_links_all_questions() # Получаем список вопросов со страницы

            for i, question_link in enumerate(links_all_questions, 1):

                print(f'Вопрос #{i} страницы {page_number}')
                print(f'Его ссылка - {question_link}')

                self.launch_browser(url=question_link) # Открываем первый вопрос
                well_waht = self.get_content_question()

                if not well_waht:

                    print('У этого вопроса нет ответов')

                self.wait()

            page_number += 1

            try:

                go_to_next_page(page_number) # Переходим на следующую страницу

            except:

                print('Такой страницы нет')
                break

        print(f'\nПарсинг закончен')
        return


    def get_links_all_questions(self):

        """ Получить ссылки на все вопросы """

        links = []
        temp = ['https://stackoverflow.com/questions/67506798/reformat-dataframe-add-rows-when-condition-is-met']

        question_links = self.browser.find_elements(
            By.XPATH,
            '//div[@id="questions"]/div[@class="question-summary"]//h3/a'
        )

        for question_link in question_links:

            address = question_link.get_attribute('href')

            links.append(address)

        return temp


    def translate_text(self, text):

        """ Переводим полученный текст и возвращаем его """

        def cut_out_excess(text):

            """ Вырезать лишние теги, которые не требуются в вопросе """

            result = re.sub(r'<aside.*?>.*?</aside>', '', str(text), flags=re.S)

            return result


        def cut_code_from_question(text):

            """ Получаем все блоки кода из вопроса и возврощаем их """

            cut_out_code = re.findall(r'<pre.+?>.+?</pre>|<hr/>|<a.+?>.+?</a>|<code.*?>.*?</code>|<blockquote.*?>.*?</blockquote>', text, flags=re.S)

            return cut_out_code


        def get_translated_text(text):

            """ Переводим текст """

            blob = TextBlob(text)
            trans = blob.translate(to='ru')

            return trans


        def remove_unnecessary_parts(text):

            """ Удаляем части текста, переводить которые не нужно.
            Возвращаем готовый текст для перевода """

            cleared_text = re.sub(r'<pre.+?>.+?</pre>|<hr/>|<a.+?>.+?</a>|<code.*?>.*?</code>|<blockquote.*?>.*?</blockquote>', 'Ё', str(text), flags=re.S)

            return cleared_text


        def insert_code_in_text(text):

            """ Вставляем код в переведенный текст """

            done_text = []
            count = 0
            text = text.split('Ё')

            for part in text:

                if count == len(cut_out_code):

                    if len(text) != len(cut_out_code):

                        done_text.append(text[-1])

                    result = ''.join(done_text)

                    return result

                done_text.append(part + cut_out_code[count])
                count += 1


        text = cut_out_excess(text)                          # Очищенный текст
        cut_out_code = cut_code_from_question(text)          # Блоки кода из вопроса
        cleared_text = remove_unnecessary_parts(text)        # Получаем 'чистый' текст для перевода
        translated_text = get_translated_text(cleared_text)  # Получаем переведенный текст
        done = insert_code_in_text(translated_text)          # Готовый вопрос

        return done


    def get_content_question(self):

        """ Собираем информацию со всего вопроса """

        def get_question_title():

            """ Получить заголовок вопроса """

            question_title = content.find('div', {'id': 'question-header'}) \
                .find('h1').find('a').text
            result = self.translate_text(question_title)

            return result


        def get_question_link():

            """ Получить ссылку вопроса """

            question_link = content.find('div', {'id': 'question-header'}) \
                .find('h1').find('a')['href']
            result = 'https://stackoverflow.com' + question_link

            return result


        def get_activity_information():

            """ Получить значения об активности вопроса """

            activity = page.find('div', {'class': 'inner-content'})

            # Вопрос задан
            question_asked = activity.find('time', {'itemprop': 'dateCreated'}).text

            # Последняя активность
            last_activity = activity.find('a', {'class': 's-link'}).text

            # Количество просмотров
            viewed = activity.find_all('span', {'class': 'fc-light'})[2] \
                .parent.text
            viewed = re.findall(r'\d+', viewed)[0]

            data = (question_asked, last_activity, viewed)

            return data


        def get_upvote_count():

            """ Получить количество голосов вопроса """

            upvote_count = full_question.find('div', {'itemprop': 'upvoteCount'}).text

            return upvote_count


        def get_question_content():

            """ Получить основное содержание вопроса """

            content = full_question.find('div', {'class': 'postcell'}) \
                .find('div', {'class': 'js-post-body'})

            translated_text = self.translate_text(content) # Переведенное содержание вопроса

            return translated_text


        def get_question_tags():

            """ Получить все теги вопроса """

            def get_description_tags(tag):

                """ Получить описание тега """

                link = f'https://stackoverflow.com/questions/tagged/{tag}'
                self.launch_browser(link)

                tag_description = self.browser.find_element(
                    By.XPATH,
                    '//div[@id="mainbar"]//p'
                ).text

                result = self.translate_text(tag_description)

                return result


            print('Смотрим теги вопроса')
            done_list_tags = []
            tags = full_question.find_all('a', {'class': 'post-tag'})

            for tag in tags:

                tag_name = tag.text
                tag_desc = get_description_tags(tag_name)
                done_list_tags.append([tag_name, tag_desc])

            return done_list_tags


        def get_related_questions():

            """ Получить похожие вопросы """

            result = []

            q_related = page.find('div', {'class': 'sidebar-related'}) \
                .find_all('div', {'class': 'spacer'})

            for q in q_related:

                vote_related_q = q.find_all('a')[0].text.strip()
                text_related_q = q.find_all('a')[1].text
                translated_text_q = self.translate_text(text_related_q) # Переводим текст
                href_related_q = q.find_all('a')[1]['href']

                result.append((vote_related_q, translated_text_q, href_related_q))

            return result


        def get_question_comments():

            """ Получить комментарии к вопросу """

            try:

                # Закрываем все возможные банеры

                #self.launch_browser(url='https://stackoverflow.com/questions/17778372/why-does-my-recursive-function-return-none')

                self.browser.find_element(
                    By.XPATH,
                    '/html/body/div[5]/div/button[1]'
                ).click()

                self.browser.implicitly_wait(5)

                self.browser.find_element(
                    By.XPATH,
                    '//*[@id="openid-buttons"]/button[4]'


                ).click()

                self.browser.implicitly_wait(5)

                # Нажимаем кнопку показать больше
                btn = self.browser.find_element(
                    By.XPATH,
                    '//div[@class="question"]//a[@class="js-show-link comments-link "]'
                )
                btn.click()

                time.sleep(2.5)

            except Exception as ex:

                # Если баннеров или вопросов больше нет - ничего не делаем
                pass

            result = []
            comments = page.find('div', {'class': 'js-post-comments-component'}) \
                .find('ul', {'class': 'comments-list'}) \
                .find_all('li')


            for comment in comments:

                c_actions = comment.find('div', {'class': 'comment-actions'}).text.strip()
                c_text = comment.find('div', {'class': 'js-comment-text-and-form'}) \
                    .text.strip()
                c_text = re.split(r'\w{3}\s\d+\s\'\d+\sat\s', c_text)[0]

                c_text_done = self.translate_text(c_text)

                result.append([c_actions, c_text_done])


            return result


        def get_question_answer():

            """ Получить все ответы к вопросу """

            def get_answer_votes(answer):

                """ Получить количество голосов ответа """

                number_votes = answer.find('div', {'class': 'votecell'}) \
                    .find('div', {'class': 'js-vote-count'}).text.strip()

                return number_votes


            def get_text_answer(answer):

                """ Получить текст ответа перевести его """

                text = answer.find('div', {'class': 'answercell'}) \
                    .find('div', {'class': 'js-post-body'})

                translated_text = self.translate_text(text) # Получаем переведенный текст

                return translated_text


            def get_content_answer(all_answers, answer_from_page):

                """ Получаем содержимое ответа """

                for i, answer in enumerate(answer_from_page, 1):

                    print(f'Ответ №{i}')

                    number_votes = get_answer_votes(answer)
                    text_answer = get_text_answer(answer)

                    all_answers.append([number_votes, text_answer])


            def get_number_pages_with_answers():

                """ Проверяем есть страницы с ответами еще """

                result = []

                try:

                    pagination = self.browser.find_elements(
                        By.XPATH,
                        '//div[@class="s-pagination pager-answers"][1]/*'
                    )

                except: # Значит страниц, с дополнительными ответами нет

                    return 0

                number_elements = len(pagination) - 1

                for n in range(1, number_elements + 1):

                    link = f'https://stackoverflow.com/questions/11/calculate-relative-time-in-c-sharp?page={n}&tab=votes#tab-top'
                    result.append(link)

                return result[1:]


            print('Смотрим ответы к вопросу')
            all_answers = []

            page = self.convert_to_bs()
            answer_from_page = page.find('div', {'id': 'answers'}) \
                .find_all('div', {'class': 'answer'}) # Все ответы с вопроса

            if answer_from_page == 0:

                return 0

            get_content_answer(all_answers, answer_from_page) # Достаем все из вопроса
            additional_p = get_number_pages_with_answers() # Узнаем, есть ли еще страницы с вопросами

            if additional_p: # Если есть дополнительные страницы с ответами

                for p in additional_p:

                    self.launch_browser(p)
                    page = self.convert_to_bs()

                    answer_from_page = page.find('div', {'id': 'answers'}) \
                        .find_all('div', {'class': 'answer'}) # Все ответы с вопроса
                    get_content_answer(all_answers, answer_from_page) # Достаем все из вопроса

            return all_answers

        # НИЖЕ ВЫЗОВ ВСЕХ ФУНКЦИЙ

        page = self.convert_to_bs()
        content = page.find('div', {'id': 'content'})
        full_question = content.find('div', {'id': 'mainbar'}) \
            .find('div', {'class': 'question'})

        q_answer = get_question_answer()
        print(q_answer)
        print(type(q_answer))
        print(len(q_answer))

        for item in q_answer:

            print(f'Голосов {item[0]}\n{item[1]}')

        if not q_answer: # Если нет ответов, то и вопрос этот не нужен

            return


        self.browser.implicitly_wait(5)

        q_title = get_question_title()
        q_link = get_question_link()
        q_activity = get_activity_information()
        q_upvote_count = get_upvote_count()
        #q_content = get_question_content()
        #q_tags = get_question_tags()
        q_related = get_related_questions()
        q_comments = get_question_comments()

        db = DataBase(
            title=q_title,
            link=q_link,
            upvote=q_upvote_count,
            related_questions=q_related,
            answers=q_answer
        )



a = Parser()
